/*
 *  Copyright 2008-2009 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file permutation_matrix.h
 *  \brief Permutation matrix format.
 */

#pragma once

#include <cusp/detail/config.h>

#include <cusp/format.h>
#include <cusp/array1d.h>
#include <cusp/detail/matrix_base.h>

namespace cusp
{

// forward definition
template <typename Array, typename IndexType, typename MemorySpace> class permutation_matrix_view;

/*! \addtogroup containers Containers 
 *  \addtogroup sparse_matrix_formats Sparse Matrices
 *  \ingroup containers
 *  \{
 */

/*! \p permutation_matrix : Permutation matrix container
 *
 * \tparam IndexType Type used for matrix indices (e.g. \c int).
 * \tparam MemorySpace A memory space (e.g. \c cusp::host_memory or cusp::device_memory)
 *
 * \note The matrix should not contain duplicate entries.
 *
 *  The following code snippet demonstrates how to create a 4-by-4
 *  \p permutation_matrix on the host then copies the
 *  matrix to the device.
 *
 *  \code
 *  #include <cusp/permutation_matrix.h>
 *  ...
 *
 *  // allocate storage for (4,4) permutation matrix
 *  cusp::permutation_matrix<int,cusp::host_memory> A(4);
 *
 *  // initialize matrix entries on host
 *  A.values[0] = 1; A.values[1] = 3; A.values[2] = 0; A.values[3] = 2;
 *
 *  // A now represents the following matrix
 *  //     [ 0 1 0 0 ]
 *  //     [ 0 0 0 1 ]
 *  //     [ 1 0 0 0 ]
 *  //     [ 0 0 1 0 ]
 *
 *  // copy to the device
 *  cusp::permutation_matrix<int,cusp::device_memory> B = A;
 *  \endcode
 *
 */
template <typename IndexType, class MemorySpace>
class permutation_matrix : public detail::matrix_base<IndexType,IndexType,MemorySpace,cusp::permutation_format>
{
  typedef cusp::detail::matrix_base<IndexType,IndexType,MemorySpace,cusp::permutation_format> Parent;
  public:
    template<typename MemorySpace2>
    struct rebind { typedef cusp::permutation_matrix<IndexType, MemorySpace2> type; };
        
    // array typedefs
    typedef typename cusp::array1d<IndexType, MemorySpace> values_array_type;
    
    /*! equivalent container type
     */
    typedef typename cusp::permutation_matrix<IndexType, MemorySpace> container;

    /*! equivalent view type
     */
    typedef typename cusp::permutation_matrix_view<typename values_array_type::view,
                                           IndexType, MemorySpace> view;
    
    /*! equivalent const_view type
     */
    typedef typename cusp::permutation_matrix_view<typename values_array_type::const_view,
                                           IndexType, MemorySpace> const_view;

    /*! Storage for the nonzero entries of the permutation data structure.
     */
    values_array_type values;

    /*! Construct an empty \p permutation_matrix.
     */
    permutation_matrix() {}

    /*! Construct a \p permutation_matrix with a specific shape and number of nonzero entries.
     *
     *  \param num_rows Number of rows.
     */
    permutation_matrix(size_t num_rows)
      : Parent(num_rows, num_rows, num_rows),
        values(num_rows) {}

    permutation_matrix(int num_rows)
      : Parent(num_rows, num_rows, num_rows),
        values(num_rows) {}

    /*! Construct a \p coo_matrix from another matrix.
     *
     *  \param matrix Another sparse or dense matrix.
     */
    template <typename MatrixType>
    permutation_matrix(const MatrixType& matrix);

    void resize(size_t num_rows)
    {
      Parent::resize(num_rows, num_rows, num_rows);
      values.resize(num_rows);
    }

    /*! Swap the contents of two \p permutation_matrix objects.
     *
     *  \param matrix Another \p permutation_matrix with the same IndexType.
     */
    void swap(permutation_matrix& matrix)
    {
      Parent::swap(matrix);
      values.swap(matrix.values);
    }

    /*! Assignment from another matrix.
     *
     *  \param matrix Another sparse or dense matrix.
     */
    template <typename MatrixType>
    permutation_matrix& operator=(const MatrixType& matrix);

    /*! Random permutation
     */
    void random_permutation( void );

}; // class permutation_matrix
/*! \}
 */


/*! \p permutation_matrix_view : Permutation matrix view
 *
 * TODO document
 *
 */
template <typename Array,
          typename IndexType   = typename Array::value_type,
          typename MemorySpace = typename Array::memory_space>
          class permutation_matrix_view : public cusp::detail::matrix_base<IndexType,IndexType,MemorySpace,cusp::permutation_format>
{
  typedef cusp::detail::matrix_base<IndexType,IndexType,MemorySpace,cusp::permutation_format> Parent;
  public:
    typedef Array values_array_type;
    
    /*! equivalent container type
     */
    typedef typename cusp::permutation_matrix<IndexType, MemorySpace> container;

    /*! equivalent view type
     */
    typedef typename cusp::permutation_matrix_view<Array, IndexType, MemorySpace> view;

    /*! View for the nonzero entries of the permutation data structure.
     */
    values_array_type values;

    // construct empty view
    permutation_matrix_view(void)
      : Parent() {}

    // construct from existing permutation matrix or view
    template <typename Matrix>
    permutation_matrix_view(Matrix& A)
      : Parent(A),
        values(A.values) {}
  
    // TODO check sizes here
    permutation_matrix_view(size_t num_rows,
                    	    Array values)
      : Parent(num_rows, num_rows, num_rows),
        values(values) {}

    void resize(size_t num_rows)
    {
      Parent::resize(num_rows, num_rows, num_rows);
      values.resize(num_rows);
    }
};

/* Convenience functions */

template <typename Array>
permutation_matrix_view<Array>
make_permutation_matrix_view(size_t num_rows,
                     	     Array values)
{
  return permutation_matrix_view<Array>
    (num_rows, values);
}

template <typename Array,
          typename IndexType,
          typename MemorySpace>
permutation_matrix_view<Array,IndexType,MemorySpace>
make_permutation_matrix_view(const permutation_matrix_view<Array,IndexType,MemorySpace>& m)
{
  return permutation_matrix_view<Array,IndexType,MemorySpace>(m);
}
    
template <typename IndexType, class MemorySpace>
typename permutation_matrix<IndexType,MemorySpace>::view
make_permutation_matrix_view(permutation_matrix<IndexType,MemorySpace>& m)
{
  return make_permutation_matrix_view
    (m.num_rows,
     make_array1d_view(m.values));
}

template <typename IndexType, class MemorySpace>
typename permutation_matrix<IndexType,MemorySpace>::const_view
make_permutation_matrix_view(const permutation_matrix<IndexType,MemorySpace>& m)
{
  return make_permutation_matrix_view
    (m.num_rows,
     make_array1d_view(m.values));
}
/*! \}
 */


} // end namespace cusp

#include <cusp/detail/permutation_matrix.inl>

